package middleware;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.StringTokenizer;

//import org.postgresql.util.PGInterval;

import logging.Logging;

// Handles the connection to a database. Runs in own thread and permanently communicates only
// with the same database as long as the system is running.
public class DatabaseHandler extends Thread {
	private Connection connection;
	/**
	 * @uml.property  name="resultString"
	 */
	private String resultString;
	private HashMap<String, PreparedStatement> hashPrepStatements;

	public DatabaseHandler (Connection conn) {
		connection = conn;	
		hashPrepStatements = new HashMap<String, PreparedStatement>();
	}

	/**
	 * @return
	 * @uml.property  name="resultString"
	 */
	public String getResultString() {
		return this.resultString;
	}

	/**
	 * Converts the ResultSet into a readable string.
	 * @param rs ResultSet
	 * @return String
	 */
	private String resultSetToString(ResultSet rs) {

		StringBuilder result = new StringBuilder();

		if (rs == null) {
			result.append("");
		} else {
			try {
				int columnSize = rs.getMetaData().getColumnCount();
				int rowSize = 0;

				// transform result into string

				// print column content
				while (rs.next()) {
					rowSize++;
					for (int i = 1; i<= columnSize; i++) {
						result.append(rs.getArray(i).toString() + " \t ");
					}
					result.append("|");
				}

			} catch (SQLException e) {
				System.err.println("Error parsing result set: " + e.getMessage());
			}
		}

		return result.toString();
	}
	
	
	/**
	 * Sets the prepared Stataments structs
	 * @param inString is the prepared Statement
	 */
	public void setPreparedStatements (String inString) {
		
		StringBuilder sb = new StringBuilder(inString);
		PreparedStatement ps = null;
		
		int index = sb.indexOf("=");
		
		String key = sb.substring(0, index);
		
		
		try {
			ps = connection.prepareStatement(sb.substring(index+1));
		} catch (SQLException e) {
			System.err.println("DBHandler was not able to create prepared statements: " + e.getMessage());
		}		
		
		hashPrepStatements.put(key, ps);
		resultString = key + " set.";
		
	}

	/**
	 * Executes specified query (on its specific server).
	 * @param inString Query string.
	 */
	public void executeQuery(String inString) {

		//Statement st = null;
		ResultSet results = null;
		PreparedStatement ps = null;

		Integer delta = 0;

		StringTokenizer st = new StringTokenizer(inString, " ");
		String key = st.nextToken();
		delta = new Integer(st.nextToken().trim());

		ps = hashPrepStatements.get(key);
		try {
			ps.setString(1, delta + " days");
			results = ps.executeQuery();
		} catch (SQLException e) {
			System.err.println("DBHandler couldn't execute Q1: " + e.getMessage());
		} 

		resultString = this.resultSetToString(results);

		try {
			if (results != null) {
				results.close();
			}
		} catch (SQLException e) {
			System.err.println("Error closing result set: " + e.getMessage());
		}

	}

	/**
	 * Executes the specified update statement (on its specific server).
	 * @param inString Update String
	 */
	public void executeUpdate(String inString) {
		
		PreparedStatement ps = null;
		Integer nrAffected = 0;


		ArrayList<PreparedStatement> listPs = new ArrayList<PreparedStatement>();
		try {
			connection.setAutoCommit(false);
			StringTokenizer bigSt = new StringTokenizer(inString, "|");
			while (bigSt.hasMoreTokens()) {
				
				StringTokenizer st = new StringTokenizer(bigSt.nextToken(), "^");
				String key = st.nextToken().trim();
				ps = hashPrepStatements.get(key);
				
				for (int i = 1; st.hasMoreTokens(); i++) {
					ps.setString(i, st.nextToken().trim());						
				}
				
				if (ps != null) {
					listPs.add(ps);
				}
			}
			
			for (int i = 0; i < listPs.size(); i++) {
				nrAffected += listPs.get(i).executeUpdate();
			}
			connection.commit();
			
			
		} catch (SQLException e) {
			System.err.println("DBHandler couldn't commit the rf: " + e.getMessage());
		}

		resultString = nrAffected.toString(); 
	}
}
